package com.ghovos.usermangerpgdemo.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ghovos.usermangerpgdemo.common.constant.RedisConst;
import com.ghovos.usermangerpgdemo.common.exception.GlobalException;
import com.ghovos.usermangerpgdemo.common.result.GlobalResultEnum;
import com.ghovos.usermangerpgdemo.common.token.TokenUtils;
import com.ghovos.usermangerpgdemo.common.utils.UserInfoJsonUtils;
import com.ghovos.usermangerpgdemo.entity.*;
import com.ghovos.usermangerpgdemo.entity.ext.UserWithRoleIds;
import com.ghovos.usermangerpgdemo.mapper.*;
import com.ghovos.usermangerpgdemo.service.UserService;
import com.ghovos.usermangerpgdemo.vo.rep.UserEntityRep;
import lombok.AllArgsConstructor;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author EdgarLiu(刘凌峰)
 * date: 2022/3/17
 * time: 下午4:00
 * email: EdgarLlf@foxmail.com
 */
@Service
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {
    private final UserMapper userMapper;
    private final UserRoleMapper userRoleMapper;
    private final RoleMapper roleMapper;
    private final RoleMenuMapper roleMenuMapper;
    private final MenuMapper menuMapper;
    private final DepartmentMapper departmentMapper;
    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 登录接口
     *
     * @param userEntity test
     * @return 是否登录成功
     */
    @Override
    public UserEntityRep login(UserEntity userEntity) {
        LambdaQueryWrapper<UserEntity> userWrapper = Wrappers.<UserEntity>lambdaQuery()
                .eq(UserEntity::getUsername, userEntity.getUsername());
        UserEntity byUsername = this.getOne(userWrapper);

        // 加密后的password
        String md5Password = DigestUtil.md5Hex(userEntity.getPassword());
        // 1. 判断用户名或密码是否正确
        if (null != byUsername
                && md5Password.equals(byUsername.getPassword())) {
            String token = TokenUtils.sign(byUsername);

            // 查询用户详情
            UserEntityRep userEntityRep = new UserEntityRep(this.getUserExt(byUsername.getId()));

            try {
                String info = new ObjectMapper().writeValueAsString(new UserWithRoleIds() {{
                    setId(userEntityRep.getId());
                    setRoleIds(userEntityRep.getRoleEntities().stream().map(RoleEntity::getId).collect(Collectors.toList()));
                }});
                stringRedisTemplate.opsForValue().getAndSet(RedisConst.ALLOW_LIST + token, info);
                stringRedisTemplate.opsForValue().getAndExpire(RedisConst.ALLOW_LIST + token, TokenUtils.EXPIRE_TIME, TimeUnit.MILLISECONDS);
                // 冗余表， 用于查询token
                stringRedisTemplate.opsForSet().add(RedisConst.USER_ID + userEntityRep.getId(), token);
                stringRedisTemplate.expire(RedisConst.USER_ID + userEntityRep.getId(), TokenUtils.EXPIRE_TIME, TimeUnit.MILLISECONDS);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }

            // 2.1 查询menu 条件
            for (RoleEntity roleEntity : userEntityRep.getRoleEntities()) {
                // 2.1.1 跟具role-menu 中间表查询 menu idList
                List<Long> menusIdList = roleMenuMapper.selectList(
                                Wrappers.<RoleMenuEntity>lambdaQuery()
                                        // 通过中间表的role id, 查询所有menu
                                        .eq(RoleMenuEntity::getRoleId, roleEntity.getId())
                        ).stream()
                        .map(RoleMenuEntity::getMenuId)
                        .collect(Collectors.toList());

                // 2.1.2 更具用户的role id 查询所有的menu entity 并放到set中去重
                if (!menusIdList.isEmpty()) {
                    Set<MenuEntity> menuEntities = new HashSet<>(menuMapper.selectBatchIds(menusIdList));
                    userEntityRep.getMenuEntityList().addAll(menuEntities);
                }

            }
            userEntityRep.setToken(token);
            return userEntityRep;
        }
        return null;
    }

    @Override
    public void logout(String token) {
        // 从白名单删除
        String json = stringRedisTemplate.opsForValue().getAndDelete(RedisConst.ALLOW_LIST + token);
        if (json != null) {
            UserWithRoleIds userWithRoleIds = UserInfoJsonUtils.toUserInfo(json);
            stringRedisTemplate.opsForSet().remove(RedisConst.USER_ID + userWithRoleIds.getId(), token);

            // 添加到黑名单
            stringRedisTemplate.opsForValue().append(RedisConst.DISABLE_LIST + token, json);
            // 添加到黑名单
            stringRedisTemplate.opsForValue().getAndExpire(RedisConst.DISABLE_LIST + token, 10 * 60 * 60L, TimeUnit.MINUTES);
        }
    }

    @Override
    public List<UserEntity> getUserExtList() {
        // 1. 查询全部用户
        List<UserEntity> userEntities = userMapper.selectList(null);
        userEntities.forEach(this::getExtendList);
        return userEntities;
    }


    @Override
    public UserEntity getUserExt(Long id) {
        // 1. 寻找userEntity
        UserEntity userEntity = userMapper.selectById(id);
        // 2. 通过公共方法查询roleList, 并存到实体中
        getExtendList(userEntity);
        return userEntity;
    }

    @Override
    public boolean addRolesList(Long id, List<Long> ids) {
        for (Long roleId : ids) {
            // 比较是否已经存在该角色
            UserRoleEntity userRoleEntity = userRoleMapper.selectOne(Wrappers.<UserRoleEntity>lambdaQuery()
                    .eq(UserRoleEntity::getUserId, id)
                    .eq(UserRoleEntity::getRoleId, roleId)
            );
            if (userRoleEntity == null) {
                // 不存在角色
                try{
                    userRoleMapper.insert(new UserRoleEntity() {{
                        setUserId(id);
                        setRoleId(roleId);
                    }});
                }catch (DuplicateKeyException e){
                    e.printStackTrace();
                    throw new GlobalException(GlobalResultEnum.SQL_UN_ERROR);
                }
            }
        }
        return true;
    }

    @Override
    public boolean deleteRolesList(Long userId, List<Long> ids) {
        LambdaQueryWrapper<UserRoleEntity> userRoleWrapper = Wrappers.<UserRoleEntity>lambdaQuery()
                .eq(UserRoleEntity::getUserId, userId)
                .in(UserRoleEntity::getRoleId, ids);
        // 1. 先查询到userInfo
        UserEntity userEntity = userMapper.selectById(userId);

        int delete = userRoleMapper.delete(userRoleWrapper);

        // 2. 刷新用户redis权限
        this.refreshUser(new UserWithRoleIds(userEntity));

        return delete > 0;
    }

    @Override
    public boolean deleteUserWithRoles(Long userId) {
        LambdaQueryWrapper<UserRoleEntity> userRoleWrapper = Wrappers.<UserRoleEntity>lambdaQuery()
                .eq(UserRoleEntity::getUserId, userId);
        // 1. 先查询到userInfo
        UserEntity userEntity = userMapper.selectById(userId);
        userRoleMapper.delete(userRoleWrapper);


        // 2. 刷新用户redis权限
        this.refreshUser(new UserWithRoleIds(userEntity));
        return this.removeById(userId);
    }

    /**
     * 公共方法 根据用户 与 role  的连接表 查询 roleList
     *
     * @param userEntity 用户实体
     */
    private void getExtendList(UserEntity userEntity) {
        // 1. 通过 user-role 中间表 寻找用户对应的ids
        List<Long> roleList = userRoleMapper.selectList(
                        Wrappers.<UserRoleEntity>lambdaQuery()
                                .eq(UserRoleEntity::getUserId, userEntity.getId())
                ).stream()
                .map(UserRoleEntity::getRoleId)
                .collect(Collectors.toList());

        // 2. 设置 role list
        if (!roleList.isEmpty()) {
            userEntity.setRoleEntities(roleMapper.selectBatchIds(roleList));
        }
        // 3. 设置 department list
        if (userEntity.getDepartmentEntity() != null) {
            userEntity.setDepartmentEntity(departmentMapper.selectById(userEntity.getDepartmentId()));
        }
    }

    /**
     * 刷新用户redis权限， 删除token
     *
     * @param userWithRoleIds 用户信息
     */
    public void refreshUser(UserWithRoleIds userWithRoleIds) {

        // 获取
        Set<String> tokens = stringRedisTemplate.opsForSet().members(RedisConst.USER_ID + userWithRoleIds.getId());
        if (tokens != null) {
            tokens.forEach(token -> {
                stringRedisTemplate.opsForValue().getAndSet(
                        RedisConst.ALLOW_LIST + token,
                        UserInfoJsonUtils.toJson(userWithRoleIds)
                );
            });
        }
    }


}
